﻿#include "wasapi.h"
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);

WasApi::WasApi(recType type)
{
	init(type);
}

void WasApi::init(recType type)
{
	CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY);
	int index = 0;
	int num = 1;
	switch (type)
	{
	case REC_SYS:
		num = 0;
		break;
	case REC_MIC:
		index = 1;
		break;
	case REC_ALL:
		break;
	case REC_NONE:
		return;
	default:
		break;
	}

	//获取扬声器与麦克风设备，存起来
	for (; index <= num; index++)
	{
		ATL::CComPtr<IMMDeviceEnumerator> pDeviceEnum;
		HRESULT hr = pDeviceEnum.CoCreateInstance(__uuidof(MMDeviceEnumerator));
		CHECK_F(hr);
		ATL::CComPtr<IMMDeviceCollection> pDeviceCollection;
		hr = pDeviceEnum->EnumAudioEndpoints(index ? eCapture : eRender, DEVICE_STATE_ACTIVE, &pDeviceCollection);
		CHECK_F(hr);

		UINT iCount = 0;
		hr = pDeviceCollection->GetCount(&iCount);
		CHECK_F(hr);

		for (UINT i = 0; i < iCount; i++)
		{
			ATL::CComPtr<IMMDevice> pDevice;
			hr = pDeviceCollection->Item(i, &pDevice);
			CHECK_F(hr);
			//ATL::CComPtr<IAudioClient> pClient;
			//hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pClient);
			//CHECK_F(hr, "audioClient");
			LPWSTR deviceid;
			pDevice->GetId(&deviceid);
			ATL::CComPtr<IPropertyStore> pProperty;
			pDevice->OpenPropertyStore(STGM_READ, &pProperty);
			PROPVARIANT pName;
			PropVariantInit(&pName);
			pProperty->GetValue(PKEY_Device_FriendlyName, &pName);

			VoiceInfo vinfo(deviceid, pName.bstrVal, index ? DEVICE_MIC : DEVICE_SYS);
			SetInfo((void*)&vinfo, VOICE);
			CoTaskMemFree(deviceid);
			PropVariantClear(&pName);
		}
	}

	//如果数组初始化太小，扩容
	int size = devicesNum;
	while (g_wasapi_ctx->GetSize(VOICE) > size)
	{
		size *= 2;
		g_wasapi_ctx->Resize(FORMAT, size);
		g_wasapi_ctx->Resize(SUCCESS, size);
	}
}

void WasApi::setFormatParam(WAVEFORMATEX* pwfx, int i)
{
	//设置参数
	FormatParam format;
	if (pwfx->wFormatTag == WAVE_FORMAT_PCM) 
	{
		format.m_captureFormat = AV_SAMPLE_FMT_S16;
	}
	
	if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) 
	{
		format.m_captureFormat = AV_SAMPLE_FMT_FLT;
	}
	else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
	{
		PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
		if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_PCM, pEx->SubFormat)) 
		{
			format.m_captureFormat = AV_SAMPLE_FMT_S16; // PCM 16 格式
		}
		else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) 
		{
			format.m_captureFormat = AV_SAMPLE_FMT_FLT;
		}
	}
	format.m_captureChannel = pwfx->nChannels;
	format.m_captureSamples = pwfx->nSamplesPerSec;
	SetInfo((void*)&format, FORMAT, i);
}

void WasApi::collecVoiceHandle(int i)
{
	CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY);
	//采集
	HRESULT hr;
	ATL::CComPtr<IMMDeviceEnumerator> pDeviceEnum = nullptr;
	hr = pDeviceEnum.CoCreateInstance(__uuidof(MMDeviceEnumerator));
	CHECK_F(hr);
	ATL::CComPtr<IMMDevice> pDevice = nullptr;
	hr = pDeviceEnum->GetDevice(GetVoiceInfo(i)->deviceid, &pDevice);
	ATL::CComPtr<IAudioClient> pClient;

	hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pClient);

	WAVEFORMATEX* pwfx = NULL;
	hr = pClient->GetMixFormat(&pwfx);

	setFormatParam(pwfx, i);

	hr = pClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
		GetVoiceInfo(i)->m_type & DEVICE_SYS ? AUDCLNT_STREAMFLAGS_LOOPBACK : 0,
		0, 0, pwfx, 0);
	WORD nBlockAlign = pwfx->nBlockAlign;
	CoTaskMemFree(pwfx);


	ATL::CComPtr<IAudioCaptureClient> pCaptureClient;
	hr = pClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pCaptureClient);
	if (FAILED(hr))
	{
		bool b = false;
		SetInfo(&b, SUCCESS, i);
		return;
	}



	hr = pClient->Start();
	DWORD last_pts = timeGetTime();
	int index = 0;
	int size[3] = { 480, 480, 480 };
	bool playing = true;
	while (g_wasapi_ctx->m_wasapiRunning)
	{
		UINT nextDataSize = 0;	
		hr = pCaptureClient->GetNextPacketSize(&nextDataSize);
		if (nextDataSize > 0 && SUCCEEDED(hr))
		{
			playing = true;
			index = index % 3;
			index++;
			BYTE* pData = NULL;
			UINT32 nSampleRead = 0;
			DWORD dwFlags = 0;
			hr = pCaptureClient->GetBuffer(&pData, &nSampleRead, &dwFlags, NULL, NULL);
			if (SUCCEEDED(hr) && nSampleRead)
			{
				GetVoiceInfo(i)->PushFrame(pData, nSampleRead * nBlockAlign);
			}
			pCaptureClient->ReleaseBuffer(nSampleRead);
			last_pts = timeGetTime();
		}
		else 
		{
			if (GetVoiceInfo(i)->m_type == DEVICE_SYS)
			{
				DWORD cur_pts = timeGetTime();
				DWORD x_pts = cur_pts - last_pts;
				if (x_pts >= 10)
				{
					if (playing)
					{
						playing = false;
					}
					else
					{
						index = index % 3;
						int s = size[index++] * 8;
						last_pts = timeGetTime();
						GetVoiceInfo(i)->PushNull(s);
					}
				}

			}		
		}
		
		Sleep(1);
	}
	
}

void WasApi::StartGetData()
{
	for (int i = 0; i < g_wasapi_ctx->GetSize(VOICE); i++)
	{
		this->go((handle)&WasApi::collecVoiceHandle, i);
	}

}

void WasApi::Stop()
{
	g_wasapi_ctx->m_wasapiRunning = false;
}

WasApi::~WasApi()
{

}